/**
 * Copyright Sp42 frank@ajaxjs.com
 * <p>
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * <p>
 * http://www.apache.org/licenses/LICENSE-2.0
 * <p>
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package cn.omisheep.commons.util;


import java.util.Arrays;
import java.util.logging.Filter;
import java.util.logging.Level;
import java.util.logging.Logger;

public class LogHelper {
    /**
     * 创建一个日志类
     *
     * @param clazz 当前日志记录的那个类
     */
    public LogHelper(Class<?> clazz) {
        className = clazz.getName().trim();
        logger    = Logger.getLogger(className);
        logger.setFilter(filter);
    }

    /**
     * 简单打印信息
     *
     * @param msg 消息，可多个消息
     */
    public static void p(Object... msg) {
        Logger.getGlobal().info(Arrays.toString(msg));
    }

    /**
     * 所在的类名
     */
    private final String className;

    /**
     * 包装这个 logger
     */
    private final Logger logger;

    /**
     * 过滤器，是否要日志服务
     */
    private final static Filter filter = record -> record.getMessage() != null && !record.getMessage().contains("no log");

    /**
     * 获取自定义的 logger。这是外界调用本类最主要的方法。例如：
     *
     * <pre>
     * private static final LogHelper LOGGER = LogHelper.getLog(SqlProvider.class);
     * </pre>
     *
     * @param clazz 被日志记录的类
     * @return 日志管理器
     */
    public static LogHelper getLog(Class<?> clazz) {
        return new LogHelper(clazz); // 如果没有，则新建一个并保存到缓存中
    }

    /**
     * 打印日志
     *
     * @param level  日志级别
     * @param color  颜色
     * @param msg    日志信息模版
     * @param params 日志信息参数
     */
    public void log(Level level, Color color, String msg, Object... params) {
        logger.logp(level, className, getMethodName(), color.str(StringUtils.format(msg, params)));
    }

    /**
     * 打印日志
     *
     * @param level  日志级别
     * @param msg    日志信息模版
     * @param params 日志信息参数
     */
    public void log(Level level, String msg, Object... params) {
        logger.logp(level, className, getMethodName(), StringUtils.format(msg, params));
    }

    /**
     * 打印日志 info等级
     *
     * @param msg    日志信息模版
     * @param params 日志信息参数
     */
    public void log(String msg, Object... params) {
        log(Level.INFO, msg, params);
    }

    /**
     * 打印一个日志。这是debug级别的。
     *
     * @param msg    日志信息模版
     * @param params 日志信息参数
     */
    public void debug(String msg, Object... params) {
        log(Level.INFO, msg, Color.GREEN, params);
    }

    /**
     * 打印一个日志。这是warn级别的。
     *
     * @param msg    日志信息模版
     * @param params 日志信息参数
     */
    public void warn(String msg, Object... params) {
        log(Level.INFO, msg, Color.GREEN, params);
    }

    /**
     * 打印一个日志。这是error级别的。
     *
     * @param msg    日志信息模版
     * @param params 日志信息参数
     */
    public void error(String msg, Object... params) {
        log(Level.SEVERE, msg, Color.RED, params);
    }

    /**
     * 打印一个日志。这是error级别的。
     *
     * @param e Throwable
     */
    public void error(Throwable e) {
        log(Level.SEVERE, e.getMessage(), Color.RED);
    }

    private String getMethodName() {
        StackTraceElement frame = null;

        for (StackTraceElement ste : Thread.currentThread().getStackTrace()) {
            String clzName = ste.getClassName();

            if (ste.isNativeMethod() || clzName.equals(Thread.class.getName()) || clzName.equals(getClass().getName()))
                continue;

            if (clzName.equals(className)) {
                frame = ste;
                break;
            }
        }

        if (frame != null) // 超链接，跳到源码所在行数
            return String.format("%s (%s:%s)", frame.getMethodName(), frame.getFileName(), frame.getLineNumber());
        else
            return null;
    }

}
